package org.googlecode.hltw.single.formula;

import java.math.BigDecimal;

import org.googlecode.hltw.single.config.server.SingleAppServerNature;
import org.googlecode.hltw.single.result.SingleAppVisitResult;

/**
 * A formula to calculate the result of singe-app-visit <br/>
 * It covers the process that a single app is visited by a single batch of
 * concurrent visitors, that is, N clients visit the server at the same moment
 * 
 * @author chenjianjx
 * 
 */
public class SingleAppVisitFormula {

	public SingleAppVisitResult calculate(SingleAppServerNature serverNature, int clientConcurrency) {

		if (clientConcurrency <= serverNature.getMaxThreadExtreme()) {
			SingleAppVisitResult result = new SingleAppVisitResult();
			result.setOverallMilis(serverNature.getResponseMilisPerRequest(clientConcurrency));
			result.setWaitResponseMilisSum(BigDecimal.valueOf(clientConcurrency).multiply(serverNature.getResponseMilisPerRequest(clientConcurrency)));
			result.setRequestCount(clientConcurrency);
			result.setResponseMilisSum(BigDecimal.valueOf(clientConcurrency).multiply(serverNature.getResponseMilisPerRequest(clientConcurrency)));
			return result;
		} else {
			SingleAppVisitResult result = new SingleAppVisitResult();
			result.setRequestCount(clientConcurrency);

			int fullLoadSubBatchSize = serverNature.getMaxThreadExtreme();
			int fullLoadSubBatchCount = clientConcurrency / fullLoadSubBatchSize;
			int lastSubBatchSize = clientConcurrency % fullLoadSubBatchSize;

			if (lastSubBatchSize == 0) {
				int servingThread = serverNature.getMaxThreadExtreme();
				BigDecimal rt = serverNature.getResponseMilisPerRequest(servingThread);
				BigDecimal responseMiliSum = BigDecimal.valueOf(fullLoadSubBatchCount * fullLoadSubBatchSize).multiply(rt);
				BigDecimal fullLoadLastEnded = BigDecimal.valueOf(fullLoadSubBatchCount).multiply(rt);
				BigDecimal waitResponse = BigDecimal.valueOf((fullLoadSubBatchCount * (fullLoadSubBatchCount + 1) / 2)).multiply(BigDecimal.valueOf(fullLoadSubBatchSize)).multiply(rt);

				result.setResponseMilisSum(responseMiliSum);
				result.setOverallMilis(fullLoadLastEnded);
				result.setWaitResponseMilisSum(waitResponse);
				return result;
			} else {

				int servingThreadPart1 = serverNature.getMaxThreadExtreme();
				BigDecimal rtPart1 = serverNature.getResponseMilisPerRequest(servingThreadPart1);
				BigDecimal responseMiliSumPart1 = BigDecimal.valueOf(fullLoadSubBatchCount * fullLoadSubBatchSize).multiply(rtPart1);
				BigDecimal fullLoadLastEnded = BigDecimal.valueOf(fullLoadSubBatchCount).multiply(rtPart1);
				BigDecimal waitResponseSumPart1 = BigDecimal.valueOf((fullLoadSubBatchCount * (fullLoadSubBatchCount + 1) / 2)).multiply(BigDecimal.valueOf(fullLoadSubBatchSize)).multiply(rtPart1);

				int servingThreadPart2 = serverNature.getMaxThreadExtreme();
				BigDecimal rtPart2 = serverNature.getResponseMilisPerRequest(servingThreadPart2);
				BigDecimal responseMiliSumPart2 = BigDecimal.valueOf(lastSubBatchSize).multiply(rtPart2);
				BigDecimal waitResponseSumPart2 = fullLoadLastEnded.add(rtPart2).multiply(BigDecimal.valueOf(lastSubBatchSize));

				result.setResponseMilisSum(responseMiliSumPart1.add(responseMiliSumPart2));
				result.setOverallMilis(fullLoadLastEnded.add(rtPart2));
				result.setWaitResponseMilisSum(waitResponseSumPart1.add(waitResponseSumPart2));

				return result;
			}

		}

	}

}
